
<template>
    <Form
        ref="formRef"
        :class="getFormClass"
        v-bind="getFormBindAttribute"
        :model="formState"
    >
        <Row v-bind="getBindRowConfig">
            <template v-for="item in getSchemas" :key="item['field']">
                <FormItem 
                    :formProps="getProps"
                    :schema="item"
                    :formActionMohted="formAction"
                    :bindModel="formState"
                    :allDefaultValues="defaultValueRef"
                    :itemChange="handleItemChange"
                >
                    <template v-for="slot in Object.keys($slots)" #[slot]="data">
                        <slot :name="slot" v-bind="data || {}"></slot>
                    </template>
                </FormItem>
            </template>
        </Row>
    </Form>
</template>
<script lang="ts">
import type { FormActionType, FormProps, FormSchema } from './types/form';
import { Form, Row, Col } from 'ant-design-vue';
import FormItem from './components/FormItem.vue';

import { basicProps } from './props';

// 工具函数
import { deepMerge } from '@/utils';
import { dateItemType } from './helper';
import { dateDayjs } from '@/utils/date';
import { cloneDeep, isArray, isObject, isString } from 'lodash-es';

import { useFormValues } from './hooks/useFormValues';
import { useAutoFocus } from './hooks/useAutoFocus';
import { useFormEvents } from './hooks/useFormEvents';

export default defineComponent({
    name: 'BasicForm',
    components: {
        Form, Row, Col, FormItem,
    },
    props: basicProps,
    emits: ['register', 'reset', 'field-value-change'],
    setup(props, { emit, attrs }) {

        const propsRef = ref<Partial<FormProps>>({}); // props传递参数对象
        const formRef = ref<Nullable<FormActionType>>(null); // 获取表单

        const formState = reactive<Recordable>({}); // 表单绑定状态值对象
        const defaultValueRef = ref<Recordable>({}); // 储存存在初始值的表单项对象
        const updateSchemas = ref<FormSchema[]>([]); // 更新后的表单项列表数据
            
        const isInitedDefaultRef = ref(false);

        // 获取props传入的数据
        const getProps = computed((): FormProps => Object.assign({}, props, unref(propsRef)) as FormProps);

        // 获取class样式
        const getFormClass = computed(() => {
            return unref(getProps).compact ? 'basic-form--compact_'+unref(getProps).compact : '';
        });

        // 获取表单绑定属性
        const getFormBindAttribute = computed(() => ({ ...attrs, ...props, ...unref(getProps) } as Recordable));

        // 获取整个表单的统一行样式和行配置
        const getBindRowConfig = computed((): Recordable => {
            const { baseRowStyle = {}, rowProps, gutter } = unref(getFormBindAttribute);
            return {
                style: baseRowStyle,
                ...rowProps,
                gutter: gutter ? gutter : 12, 
            };
        });

        // 获取表单项列表数据
        const getSchemas = computed((): FormSchema[] => {
            const schemas: FormSchema[] = unref(updateSchemas) && unref(updateSchemas).length ? unref(updateSchemas) : ((unref(getProps)?.schemas || []) as any[]);
            return cloneDeep(schemas as FormSchema[]);
        });

        const { handleFormValues, initDefault } = useFormValues({
            getProps,
            defaultValueRef,
            getSchema: getSchemas,
            formModel: formState,
            getBindConfing: getFormBindAttribute,
        });

        // 获取表单操作方法
        const {
            setFieldsValue,
            clearValidate,
            validate,
            validateFields,
            getFieldsValue,
            updateSchema,
            resetSchema,
            appendSchemaByField,
            removeSchemaByField,
            resetFields,
            needsResetFields,
            scrollToField,
        } = useFormEvents({
            emit: (emit as EmitType),
            getProps,
            formModel: formState,
            getSchema: getSchemas,
            defaultValueRef: defaultValueRef,
            formElRef: formRef as Ref<FormActionType>,
            schemaRef: updateSchemas as Ref<FormSchema[]>,
            handleFormValues,
        });

        useAutoFocus({
            getSchema: getSchemas,
            getProps,
            isInitedDefault: isInitedDefaultRef,
            formElRef: formRef as Ref<FormActionType>,
        });
        
        // 更新表单项
        function handleItemChange (key: string, value: any) {

            formState[key] = value;

            const { validateTrigger } = unref(getFormBindAttribute); // 获取定义的触发事件类型

            if (!validateTrigger || validateTrigger === 'change') {

                validateFields([key]).catch(() => {}); // 校验对应项；
            }

            emit('field-value-change', key, value);
        };

        /**
         * 设置表单属性
         * @param formProps 表单属性的部分值
         * @returns 无返回值，但会更新表单的属性
         */
        async function setProps(formProps: Partial<FormProps>): Promise<void> {
            propsRef.value = deepMerge(unref(propsRef), formProps);
            // console.log('setProps', unref(propsRef));
        }

        // 初始化表单属性
        const formAction: Partial<FormActionType> = {
            setProps,
            getFieldsValue,
            setFieldsValue,
            resetFields,
            needsResetFields,
            updateSchema,
            resetSchema,
            appendSchemaByField,
            removeSchemaByField,
            clearValidate,
            validateFields,
            validate,
            scrollToField,
        };

        onMounted(() => {
            // 初始化处理表单数据
            initDefault();
            // 调用抛出事件并写入父类传值数据
            emit('register', formAction);
        });

        onBeforeUnmount(async() => {
            if(Object.keys(formState).length) Object.keys(formState).forEach(key => delete formState[key]);
            // console.log('卸载前：',formState);
        });

        // 处理 监听传递数据 事件
        watch( () => unref(getProps).model, () => {
            const { model } = unref(getProps);
            if (!model) return;
            setFieldsValue(model);

        }, { immediate: true });

        watch(() => unref(getProps).schemas, (schemas) => {

            resetSchema(schemas ?? []); // 重置表单配置项列表数据
        });

        watch(() => getSchemas.value, (schema) => {

            if (unref(isInitedDefaultRef)) return;

            if (schema?.length) {
                isInitedDefaultRef.value = true;

                initDefault();
            }
        });

        return {
            formRef,
            defaultValueRef,
            formState,
            getFormClass,
            getFormBindAttribute,
            getBindRowConfig,
            getSchemas,
            getProps,
            formAction,
            handleItemChange,
        };
    },
});
</script>

<style lang="less" scoped>

</style>